#include "LecteurPhraseSimple.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

   //-------------------------------- Constructeur -----------------------------
   LecteurPhraseSimple::LecteurPhraseSimple(string nomFich) :
	ls(nomFich) {
   }

   //------------------------------ Analyse du texte ---------------------------
   void LecteurPhraseSimple::analyse() {                        // méthode qui analyse la syntaxe. Si incorrecte, programme interrompu (exit)
	programme();
	cout << "Syntaxe correcte." << endl;
   }

///-----------------------Implémentation de la grammaire---------------------///

   void LecteurPhraseSimple::programme() {      // <programme> ::= debut <seqInst> fin FIN_FICHIER
	sauterSymCour("debut");                 // on regarde si le mot clé 'debut' est le premier élément du fichier
	seqInst();                              // on regarde si on est dans une instruction
	sauterSymCour("fin");                   // 
	testerSymCour("<FINDEFICHIER>");        // 
}

   //------------------------- Séquence d'instructions -------------------------
   void LecteurPhraseSimple::seqInst() {        // <seqInst> ::= <inst> ; { <inst> ; }

	do {    inst();
		sauterSymCour(";");
	} while (ls.getSymCour()=="<VARIABLE>" ||
                 ls.getSymCour()=="si" ||
                 ls.getSymCour()=="tantque" ||
                 ls.getSymCour()=="repeter" ||
                 ls.getSymCour()=="lire" ||
                 ls.getSymCour()=="ecrire" ||
                 ls.getSymCour()=="pour"
                );
	// tant que le symbole courant est un debut possible d'instruction...
   }

   //------------------------------ Instructions -------------------------------

   void LecteurPhraseSimple::inst() {           // <inst>       ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instLire> | <instEcrire>
	if (ls.getSymCour()==("<VARIABLE>"))
            affectation();                              // appel à la méthode affectation()
        else if (ls.getSymCour()==("si"))
            instSi();
        else if (ls.getSymCour()==("tantque"))
            instTq();
        else if (ls.getSymCour()==("repeter"))
            instRepeter();
        else if (ls.getSymCour()==("lire"))             
            instLire();                                 // appel à la méthode instLire permettant de lire une variable (type <VARIABLE>) au clavier
        else if (ls.getSymCour()==("ecrire"))           
            instEcrire();                               // appel à la méthode instEcrire, permettant l'affichage à l'ecran de variables, entiers, chaines.
        else if (ls.getSymCour()==("pour"))           
            instPour(); 
        else
		erreur("<inst>");
   }
   //----------------------------- Instructions SI -----------------------------
   
  void LecteurPhraseSimple::instSi() {          // <instSi>     ::= si ( <expBool> ) <seqInst> { sinonsi ( <expBool> ) <seqInst> } [ sinon <seqInst> ] finsi
      
        sauterSymCour("si");
        sauterSymCour("(");
        expBoolOU();
        sauterSymCour(")");

        seqInst();
               
       while (ls.getSymCour()==("sinonsi")) {
          sauterSymCour("sinonsi");
          sauterSymCour("(");
          expBoolOU();
          sauterSymCour(")");
          seqInst();
        }
                
        if (ls.getSymCour()=="sinon") {
           sauterSymCour("sinon");
           seqInst();   
        }
        sauterSymCour("finsi");
  }
  
     //----------------------------- Instructions TQ -----------------------------

  void LecteurPhraseSimple::instTq() {          // <instTq>     ::= tantque ( <expBool> ) <seqInst> fintantque
      
       sauterSymCour("tantque");
       sauterSymCour("(");
       expBoolOU();
       sauterSymCour(")");
       seqInst();
       sauterSymCour("fintantque");
      
  }
  
       //------------------------- Instructions Repeter ------------------------

  void LecteurPhraseSimple::instRepeter() {     // <instRepeter>::= repeter <seqInst> jusqua ( <expBool> )
       sauterSymCour("repeter");
       seqInst();
       sauterSymCour("jusqua");
       sauterSymCour("(");
       expBoolOU();
       sauterSymCour(")");
       
  }
  
       //------------------------- Instructions Lire ---------------------------
  
  void LecteurPhraseSimple::instLire()  {      // <instLire>   ::= lire ( <variable>) 
       sauterSymCour("lire");
       sauterSymCour("(");
       if (ls.getSymCour()=="<VARIABLE>") {
                facteur();
                sauterSymCour(")");
        }
        
        else
                erreur("<variable>");
   } 
  
       //----------------------- Instructions Ecrire ---------------------------
  
  void LecteurPhraseSimple::instEcrire(){      // <instEcrire> ::= ecrire ( <chaine> | <expression> )  
       
        sauterSymCour("ecrire");
        sauterSymCour("(");
        
        if (ls.getSymCour()=="<CHAINE>") {
                sauterSymCour("<CHAINE>");
        }
        else
                expression();
        
        sauterSymCour(")");
    
   }
       //----------------------- Instructions Pour ---------------------------
  
  void LecteurPhraseSimple::instPour(){         // <instPour>   ::= pour ( affectation ; <expBoolOU>; affectation ) <seqInst> finpour 
      
      sauterSymCour("pour");
      sauterSymCour("(");
      
      affectation();
      sauterSymCour(";");
      
      expBoolOU();
      
      sauterSymCour(";");
      affectation();
      sauterSymCour(")");
      
      seqInst();
      sauterSymCour("finpour");     

  } 
  
   //------------------------------- Affectation -------------------------------

   void LecteurPhraseSimple::affectation() {    // <affectation> ::= <variable> = <expression>

	sauterSymCour("<VARIABLE>");            // on test si le premier élément est une "VARIABLE"
	sauterSymCour("=");                     // on teste si l'élément suivant est un "="
	expression();                           // on teste si l'élément suivant est une expression: appel de la méthode expression
   }

   //------------------------------- Expression --------------------------------

   void LecteurPhraseSimple::expression() {     // <expression> ::= <terme> {<opAdd><terme>}

	terme();                                // appel de la méthode terme pour tester si le premier élément en est un
	while ( ls.getSymCour()=="+" ||         // tant que le symbole lu est un '+'
                ls.getSymCour()=="-" ) {        // ou un '-'
		opAdd();                        // on appelle la méthode opAdd() pour continuer l'analyse
		terme();                        // on appelle la méthode terme()
	}
   }

   //---------------------------------- Terme ----------------------------------

   void LecteurPhraseSimple::terme() {          // <terme> ::= <facteur> {<opMult><facteur>}

	facteur();                              // appel de la méthode facteur pour savoir si le premier élément en est un 
	while ( ls.getSymCour()=="*" ||         // tant que le symbole lu est un '*'
                ls.getSymCour()=="/" ) {        // ou un '/'
		opMult();                       // on appelle la méthode opMult() pour continuer l'analyse
		facteur();                      // on appelle la méthode facteur()
	}
   }

   //--------------------------------- Facteur ---------------------------------

   void LecteurPhraseSimple::facteur() {        // <facteur> ::=
   //   <entier>  |  <variable>  |  <opUnaire>  <expBoolOU>  |  ( <expBoolOU> )

	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>")
		ls.suivant();
        
	else if (ls.getSymCour()=="-" || ls.getSymCour()=="non") {
		opUnaire();
		expBoolOU();
               
                
	} else if (ls.getSymCour()=="(") {
                sauterSymCour("(");
		expBoolOU();
		sauterSymCour(")");
                
	} else
		erreur("<facteur>");
   }

   //-------------------------------- [ + | - ] --------------------------------

   void LecteurPhraseSimple::opAdd() {          // <opAdd> ::= + | -

	if (    ls.getSymCour()=="+" ||         // si le symbole lu est un '+'
                ls.getSymCour()=="-" )          // ou '-'
		ls.suivant();                   // on passe au suivant
	else                                    // sinon
		erreur("<opAdd>");              // on affiche une erreur avec détails
   }

   //-------------------------------- [ * | / ] --------------------------------

   void LecteurPhraseSimple::opMult() {         // <opMult> ::= * | /

	if (    ls.getSymCour()=="*" ||         // si le symbole lu est un '*'
                ls.getSymCour()=="/")           // ou un '/'
		ls.suivant();                   // on passe au suivant
	else                                    // sinon
		erreur("<opMult>");             // on affiche une erreur avec détails
   }

   //-------------------------- Expressions booléennes ET -------------------------

   void LecteurPhraseSimple::expBoolET() {      // <expBoolET>    ::= <relation> { <opBoolET> <relation> }
        relation();
        while ( ls.getSymCour()=="et") {        // si symbole courant est 'et'
		opBoolET();                     // appel de la méthode opBoolET() pour continuer l'analyse
		relation();                     // appel de la méthode relation pour tester l'élément suivant
        }
   }    
   
   
   //-------------------------- Expressions booléennes OU -------------------------

   void LecteurPhraseSimple::expBoolOU() {      // <expBoolOU>    ::= <expBoolET> { <opBoolOU> <expBoolET> }
        expBoolET();
        while ( ls.getSymCour()=="ou") {        // si symbole courant est 'ou'
		opBoolOU();                     // appel de la méthode opBoolOU() pour continuer l'analyse
		expBoolET();                    // appel de la méthode expBoolET
        }
   }    
   
   //---------------------------------- [ ou ] -------------------------------

   void LecteurPhraseSimple::opBoolOU() {         // <opBool> ::= ou

        if      (ls.getSymCour()=="ou" )         // si le symbole courant est un 'ou'
                        ls.suivant();            // on passe au suivant
        else                                    // sinon
		erreur("<opBoolOU>");             // on appelle la methode erreur avec le message "<opBool>"
}
   
      //------------------------------- [ et ] -------------------------------

   void LecteurPhraseSimple::opBoolET() {         // <opBool> ::= et

        if (    ls.getSymCour()=="et" )         // si le symbole courant est un 'et'
		ls.suivant();                   // on passe au suivant
        else                                    // sinon
		erreur("<opBoolET>");           // on appelle la methode erreur avec le message "<opBool>"
 }       
   //-------------------------------- Relation ---------------------------------

   void LecteurPhraseSimple::relation() {       // <relation> ::=
                                                // <expression> {<opRel> <expression>}

        expression();                           // appel de la méthode expression pour vérifier la syntaxe
        
        while ( ls.getSymCour()=="==" ||        // tant que le symbole lu est un '=='
                ls.getSymCour()=="!=" ||        // ou '!='
                ls.getSymCour()=="<="  ||        // ou '<'
                ls.getSymCour()=="<" ||        // ou '<='
                ls.getSymCour()==">="  ||        // ou '>'
                ls.getSymCour()==">") {        // ou '>='
        opRel();                                // appel de la méthode opRel() pour continuer l'analyse
        expression();                           // appel de la méthode expression pour vérifier la syntaxe
        }
   }

   //---------------------- [ == | != | < | <= | > | >= ] ----------------------

   void LecteurPhraseSimple::opRel() { // <opRel> ::= == | != | < | <= | > | >=
       
        if (    ls.getSymCour()=="==" ||
                ls.getSymCour()=="!=" ||
                ls.getSymCour()=="<=" ||
                ls.getSymCour()=="<"  ||
                ls.getSymCour()==">=" ||
                ls.getSymCour()==">"  )
                ls.suivant();
        else
                erreur("<opRel>");
   }

   //------------------------------ [ - | non ] --------------------------------

   void LecteurPhraseSimple::opUnaire(){ // <opUnaire> ::= - | non
       
        if (    ls.getSymCour()=="-" ||
                ls.getSymCour()=="non")
                ls.suivant();
        else
                erreur("<opUnaire>");
   }

   //---------------------------------------------------------------------------

   void LecteurPhraseSimple::testerSymCour(string ch) {
       
        if (ls.getSymCour() != ch) {
                cout << endl << "-------- Erreur ligne " << ls.getLigne()
		<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
		<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
		<< endl;
		exit(0); // plus tard, on levera une exception
	}
   }

   //---------------------------------------------------------------------------
   
   void LecteurPhraseSimple::sauterSymCour(string ch) {
       
        testerSymCour(ch);
	ls.suivant();
}

//------------------------------------------------------------------------------
   
   void LecteurPhraseSimple::erreur(string mess) {
        cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
	<< ls.getColonne() << endl << "   Attendu : " << mess << endl
	<< "   Trouve  : " << ls.getSymCour() << endl << endl;
	exit(0); // plus tard, on levera une exception
}